package com.atguigu.gmall.cart.service.impl;

import com.atguigu.gmall.cart.mapper.CartInfoMapper;
import com.atguigu.gmall.cart.service.CartAsyncService;
import com.atguigu.gmall.cart.service.CartService;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.util.DateUtil;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CartServiceImpl implements CartService {

    @Autowired
    private CartInfoMapper cartInfoMapper;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private CartAsyncService cartAsyncService;


    /**
     * 添加商品到购物车
     * @param skuId
     * @param userId
     * @param skuNum
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addToCart(Long skuId, String userId, Integer skuNum) {
        /*
            1.  添加商品之前，先看一下购物车中是否有该商品
                true:
                    商品数量相加
                false:
                    直接加入购物车
            2.    将数据同步到redis！

         */
        String cartKey = getCartKey(userId);
        if (!redisTemplate.hasKey(cartKey)) {
            //  加载数据库并放入缓存
            this.loadCartCache(userId);
        }
       /* QueryWrapper<CartInfo> cartInfoQueryWrapper = new QueryWrapper<>();
        cartInfoQueryWrapper.eq("user_id",userId);
        cartInfoQueryWrapper.eq("sku_id",skuId);
        CartInfo cartInfoExist = cartInfoMapper.selectOne(cartInfoQueryWrapper);*/
        CartInfo cartInfoExist = null;
        try {
            cartInfoExist = (CartInfo) redisTemplate.boundHashOps(cartKey).get(skuId.toString());

            // 已存在购物项
            if (cartInfoExist != null) {
                //  数量相加
                cartInfoExist.setSkuNum(cartInfoExist.getSkuNum()+skuNum);
                //  初始化实时价格
                //  本质skuPrice = skuInfo.price
                cartInfoExist.setSkuPrice(productFeignClient.getSkuPrice(skuId));
                //  修改更新时间
                cartInfoExist.setUpdateTime(new Timestamp(new Date().getTime()));
                //  再次添加商品时，默认选中状态。
                cartInfoExist.setIsChecked(1);

    //            cartInfoMapper.updateById(cartInfoExist);
                // 异步更新
                cartAsyncService.updateCartInfo(cartInfoExist);

            } else {
                //  第一次添加购物车
                CartInfo cartInfo = new CartInfo();
                SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
                cartInfo.setUserId(userId);
                cartInfo.setSkuId(skuId);
                //  在初始化的时候，添加购物车的价格 = skuInfo.price
                cartInfo.setCartPrice(skuInfo.getPrice());
                //  数据库不存在的，购物车的价格 = skuInfo.price
                cartInfo.setSkuPrice(skuInfo.getPrice());
                cartInfo.setSkuNum(skuNum);
                cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
                cartInfo.setSkuName(skuInfo.getSkuName());
                cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
                cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));

    //            cartInfoMapper.insert(cartInfo);

                // 异步更新
                cartAsyncService.saveCartInfo(cartInfo);
    //            int i = 1 / 0;
                cartInfoExist = cartInfo;
            }
        } catch (Exception e) {
            System.out.println("catch===================================================");
            e.printStackTrace();
            return;
        }
        System.out.println("==============================================================");
        // 问题1. java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.String skuId->String
        redisTemplate.opsForHash().put(cartKey, skuId.toString(), cartInfoExist);
        setCartKeyExpire(cartKey);
    }

    /**
     * 查询所有的购物车
     * @param userId
     * @param userTempId
     * @return
     */
    @Override
    public List<CartInfo> getCartList(String userId, String userTempId) {
        // 合并的购物车数据
        List<CartInfo> cartInfoList = new ArrayList<>();
        // 登录的购物车数据
        List<CartInfo> cartInfoLoginList = getCartList(userId);
        // 未登录的购物车数据
        List<CartInfo> cartInfoNoLoginList = getCartList(userTempId);

        // userId是空/""表示未登录
        if (StringUtils.isEmpty(userId)) {
            // 返回临时用户Id 获取未登录的购物车数据
            return cartInfoNoLoginList;
        }

        // 已登录但是userTempId是空
        if (StringUtils.isEmpty(userTempId)) {
            // 返回登录的购物车数据
            return cartInfoLoginList;
        } else { // 已登录并且userTempId不是空
            // 未登录的购物车数据是空
            if (CollectionUtils.isEmpty(cartInfoNoLoginList)) {
                // 返回登录的购物车数据
                return cartInfoLoginList;
            } else { // 未登录的购物车数据不是空
                // 说明未登录购物车数据有值！需要发生合并操作！
                cartInfoList = mergeToCartList(cartInfoNoLoginList,userId);
                //  删除临时购物车数据：
                deleteCartList(userTempId);
            }
        }
       return cartInfoList;

    }

    /**
     * 合并登录和未登录的购物车的数据
     * @param cartInfoNoLoginList
     * @param userId
     * @return
     */
    private List<CartInfo> mergeToCartList(List<CartInfo> cartInfoNoLoginList, String userId) {
        List<CartInfo> cartInfoLoginList = this.getCartList(userId);

        Map<Long, CartInfo> longCartInfoMap = cartInfoLoginList.stream().collect(Collectors.toMap(CartInfo::getSkuId, cartInfo -> cartInfo));

        for (CartInfo cartInfo : cartInfoNoLoginList) {
            //  未登录购物车购物项的skuId
            Long skuId = cartInfo.getSkuId();
            if (longCartInfoMap.containsKey(skuId)){
                //  数量相加 37,38 数量相加
                CartInfo cartLoginInfo = longCartInfoMap.get(skuId);
                cartLoginInfo.setSkuNum(cartLoginInfo.getSkuNum()+cartInfo.getSkuNum());
                //  选中状态 isChecked = 1： 还想多买点东西！
                if (cartInfo.getIsChecked().intValue()==1){
                    cartLoginInfo.setIsChecked(1);
                }
                UpdateWrapper<CartInfo> updateWrapper = new UpdateWrapper<>();
                updateWrapper.eq("user_id",cartLoginInfo.getUserId());
                updateWrapper.eq("sku_id",cartLoginInfo.getSkuId());

                cartInfoMapper.update(cartLoginInfo,updateWrapper);
            } else {
                //  39 1 插入数据操作！
                //  但是临时购物车数据的userId 是类似于一个uuid 的东西！
                cartInfo.setUserId(userId);
                //  cartInfoMapper.insert(cartInfo);
                cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
                cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
                // cartAsyncService.saveCartInfo(cartInfo);    //  后执行！    建议同步！ 建议redis 没有过期时间！
                cartInfoMapper.insert(cartInfo);
            }
        }

        //  最终要返回 37,38,39 :
        List<CartInfo> cartInfoList = loadCartCache(userId); // 查询都是走缓存！
        return cartInfoList;
    }

    /**
     * 删除临时购物车数据和缓存的数据
     * @param userTempId
     */
    private void deleteCartList(String userTempId) {
        cartAsyncService.deleteCartInfo(userTempId);
        //  redis.del(key);
        String cartKey = this.getCartKey(userTempId);
        if (redisTemplate.hasKey(cartKey)){
            redisTemplate.delete(cartKey);
        }
    }



    /**
     * 根据用户获取购物车
     * @param userId
     * @return
     */
    private List<CartInfo> getCartList(String userId) {
        // 声明一个返回的集合对象
        List<CartInfo> cartInfoList = new ArrayList<>();
        if (StringUtils.isEmpty(userId)) return cartInfoList;

        /*
            1.  根据用户Id 查询 {先查询缓存，缓存没有，再查询数据库}
        */
        // 定义key user:userId:cart
        String cartKey = this.getCartKey(userId);
        // 获取数据
        cartInfoList = redisTemplate.opsForHash().values(cartKey);
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            // 购物车列表显示有顺序：按照商品的更新时间 降序
            cartInfoList.sort(new Comparator<CartInfo>() {
                @Override
                public int compare(CartInfo o1, CartInfo o2) {
                    return DateUtil.truncatedCompareTo(o2.getUpdateTime(), o1.getUpdateTime(), Calendar.SECOND);

                }
            });
            return cartInfoList;
        } else {
            // 缓存中没有数据！
            cartInfoList = loadCartCache(userId);
            return cartInfoList;
        }

    }

    /**
     * 根据用户Id 查询数据并放入缓存！
     * @param userId
     * @return
     */
    public List<CartInfo> loadCartCache(String userId) {

        QueryWrapper<CartInfo> cartInfoQueryWrapper = new QueryWrapper<>();
        cartInfoQueryWrapper.eq("user_id", userId);
        cartInfoQueryWrapper.orderByDesc("update_time");

        List<CartInfo> cartInfoList = cartInfoMapper.selectList(cartInfoQueryWrapper);
        if (CollectionUtils.isEmpty(cartInfoList)) {
            return new ArrayList<>();
        }

        HashMap<String, CartInfo> map = new HashMap<>();
        for (CartInfo cartInfo : cartInfoList) {
            //  当缓存中的购物车数据为空的时候，当前购物车的价格可能会发生变动！所以要查询一下实时价格！
            cartInfo.setSkuPrice(productFeignClient.getSkuPrice(cartInfo.getSkuId()));
            map.put(cartInfo.getSkuId().toString(), cartInfo);
        }
        String cartKey = getCartKey(userId);
        redisTemplate.opsForHash().putAll(cartKey, map);
        setCartKeyExpire(cartKey);
        return cartInfoList;
    }



    // 设置cartKey过期时间
    private void setCartKeyExpire(String cartKey) {
        redisTemplate.expire(cartKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }

    // 获取购物车的key
    private String getCartKey(String userId) {
        //定义key user:userId:cart
        return RedisConst.USER_KEY_PREFIX + userId +       RedisConst.USER_CART_KEY_SUFFIX;
    }

    @Override
    public void checkCart(String userId, Integer isChecked, Long skuId) {
        //  操作数据库
        cartAsyncService.checkCart(userId,isChecked,skuId);
        //  同步redis！获取购物车key
        String cartKey = this.getCartKey(userId);
        //  获取修改的cartInfo   hget key field  field = skuId
        CartInfo cartInfo = (CartInfo) redisTemplate.opsForHash().get(cartKey, skuId.toString());
        if (cartInfo!=null){
            //  变更状态
            cartInfo.setIsChecked(isChecked);
            //  将变更之后的数据放回缓存！ hset key field value;
            redisTemplate.opsForHash().put(cartKey,skuId.toString(),cartInfo);

            //  可以考虑是否需要从新设置过期时间！
            this.setCartKeyExpire(cartKey);
        }
    }

    @Override
    public void deleteCart(Long skuId, String userId) {
        cartAsyncService.deleteCartInfo(userId, skuId);
        String cartKey = getCartKey(userId);

        //获取缓存对象
        BoundHashOperations<String, String, CartInfo> hashOperations = redisTemplate.boundHashOps(cartKey);
        if (hashOperations.hasKey(skuId.toString())){
            hashOperations.delete(skuId.toString());
        }
    }

    @Override
    public List<CartInfo> getCartCheckedList(String userId) {
        List<CartInfo> cartInfoList = new ArrayList<>();

        // 定义key user:userId:cart
        String cartKey = this.getCartKey(userId);
        List<CartInfo> cartCacheInfoList = redisTemplate.opsForHash().values(cartKey);
        if (null != cartCacheInfoList && cartCacheInfoList.size() > 0) {
            for (CartInfo cartInfo : cartCacheInfoList) {
                // 获取选中的商品！
                if (cartInfo.getIsChecked().intValue() == 1) {
                    cartInfoList.add(cartInfo);
                }
            }
        }
        return cartInfoList;
    }


}
